package com.yyfax.pay.depository.service.inner.impl;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import com.yyfax.commons.lang.DateUtil;
import com.yyfax.framework.common.Log;
import com.yyfax.pay.common.enums.FuiouBusiTypeEnum;
import com.yyfax.pay.common.enums.FuiouDepositStateEnum;
import com.yyfax.pay.common.exception.PayExceptionCode;
import com.yyfax.pay.common.model.vo.PayRspCodeVO;
import com.yyfax.pay.common.util.FuiouIdUtils;
import com.yyfax.pay.depository.dao.FuiouWithdrawDao;
import com.yyfax.pay.depository.dao.FuiouWithdrawModifyCashDao;
import com.yyfax.pay.depository.domain.FuiouWithdraw;
import com.yyfax.pay.depository.domain.FuiouWithdrawModifyCash;
import com.yyfax.pay.depository.service.inner.FuiouDepositCommonService;
import com.yyfax.pay.depository.service.inner.FuiouWithdrawService;
import com.yyfax.pay.depository.vo.FuiouWithdrawModifyCashVO;
import com.yyfax.pay.third.facade.fuioudeposit.FuiouDepositEntrustService;
import com.yyfax.pay.third.facade.fuioudeposit.FuiouDepositService;
import com.yyfax.pay.vo.depository.ModifyWithdrawCashWayReq;
import com.yyfax.pay.vo.depository.WithdrawReq;

/**
 * @author yejc
 * @date 2017-09-07
 * @since v1.0.0
 */
@Service
public class FuiouWithdrawServiceImpl implements FuiouWithdrawService {

	protected Logger logger = LoggerFactory.getLogger(getClass());
	@Resource
	private FuiouIdUtils fuiouIdUtils;
	@Resource
	private FuiouWithdrawDao fuiouWithdrawDao;

	@Resource
	private FuiouWithdrawModifyCashDao fuiouWithdrawModifyCashDao;

	@Resource
	private FuiouDepositService fuiouDepositService;
	@Resource
	private FuiouDepositCommonService fuiouDepositCommonService;
	@Resource
	private FuiouDepositEntrustService fuiouDepositEntrustService;

	@Override
	public FuiouWithdraw entrustWithdraw(WithdrawReq req, String merchantCode) {
		FuiouWithdraw order = initFuiouWithdrawOrder(req, merchantCode);

		int i = fuiouWithdrawDao.updateStateByBusiId(FuiouDepositStateEnum.INI.getValue(), FuiouDepositStateEnum.DEALING.getValue(),
				order.getBusiId(), DateUtil.getCurrentDateTime());
		if (i > 0) {
			// 请求存管系统
			PayRspCodeVO payRspCodeVO = fuiouDepositEntrustService.entrustWithdraw(order);

			// 处理返回结果
			handleEntrustWithdrawResponse(payRspCodeVO, order);
		}

		return fuiouWithdrawDao.selectByPrimaryKey(order.getBusiId());
	}

	@Override
	public FuiouWithdraw withdrawQuery(String busiId) {
		final String op = "FuiouWithdrawServiceImpl.withdrawQuery";
		logger.info(Log.op(op).msg("【富友存管】提现查询开始！").kv("busiId", busiId).toString());
		FuiouWithdraw withdraw = fuiouWithdrawDao.selectByPrimaryKey(busiId);

		PayRspCodeVO result = fuiouDepositService.withdrawQuery(withdraw);

		String state = FuiouDepositStateEnum.FAIL.getValue();

		if (PayExceptionCode.SUCCESS.isCode(result.getCode())) {
			state = FuiouDepositStateEnum.SUCCESS.getValue();
		} else if (PayExceptionCode.WARN_REQ_TIME_OUT.isCode(result.getCode())) {
			// 如果请求超时，则更新状态为处理中
			state = FuiouDepositStateEnum.DEALING.getValue();
		}

		// 更新订单
		logger.info(Log.op(op).msg("【富友存管】提现查询更新订单").kv("busiId", busiId).kv("state", state).toString());
		fuiouWithdrawDao.updateStateAndRspByBusiId(state, withdraw.getBusiId(), DateUtil.getCurrentDateTime(), result.getCode(), result.getDesc());

		// 返回最终状态
		FuiouWithdraw order = fuiouWithdrawDao.selectByPrimaryKey(busiId);
		logger.info(Log.op(op).msg("【富友存管】提现查询完成！").kv("order", order).toString());
		return order;
	}

	private void handleEntrustWithdrawResponse(PayRspCodeVO resp, FuiouWithdraw order) {
		final String op = "FuiouWithdrawServiceImpl.handleEntrustWithdrawResponse";
		// 超时
		if (PayExceptionCode.WARN_REQ_TIME_OUT.isCode(resp.getCode())) {
			logger.warn(Log.op(op).msg("委托提现请求超时").kv("busiId", order.getBusiId()).toString());
			fuiouDepositCommonService.saveQueryTask(order.getMerchantCode(), order.getBusiId(), FuiouBusiTypeEnum.WITHDRAW_QUERY);
			throw PayExceptionCode.WARN_REQ_TIME_OUT.exp();
		}

		if (PayExceptionCode.SUCCESS.isCode(resp.getCode())) {
			// 更新订单处理状态为已受理，还要等待back_notify
			fuiouWithdrawDao.updateStateAndRspByBusiId(FuiouDepositStateEnum.ACCEPTED.getValue(), order.getBusiId(), DateUtil.getCurrentDateTime(),
					resp.getCode(), resp.getDesc());
		} else {
			// 更新订单处理状态为失败
			fuiouWithdrawDao.updateStateAndRspByBusiId(FuiouDepositStateEnum.FAIL.getValue(), order.getBusiId(), DateUtil.getCurrentDateTime(),
					resp.getCode(), resp.getDesc());
		}
	}

	/**
	 * 初始化委托提现订单
	 *
	 * @author yejc
	 * @date 2017-09-30
	 * @since v2.0.0
	 * @param req
	 * @param merchantCode
	 * @return
	 */
	private FuiouWithdraw initFuiouWithdrawOrder(WithdrawReq req, String merchantCode) {
		final String op = "FuiouWithdrawServiceImpl.initFuiouWithdrawOrder";
		FuiouWithdraw order = new FuiouWithdraw();
		order.setOrderTime(req.getOrderTime());
		order.setOrderId(req.getOrderId());
		order.setMerchantCode(merchantCode);
		order.setState(FuiouDepositStateEnum.INI.getValue());
		order.setTerminal(req.getTerminal());
		order.setUserId(req.getUserId());
		String now = DateUtil.getCurrentDateTime();
		order.setCreateTime(now);
		order.setUpdateTime(now);
		order.setType(FuiouBusiTypeEnum.ENTRUST_WITHDRAW.getValue());
		order.setBusiId(fuiouIdUtils.generateBusiId(FuiouBusiTypeEnum.ENTRUST_WITHDRAW, merchantCode));
		order.setLoginId(req.getAccountNo());
		order.setAmount(req.getAmount());
		order.setBackUrl(req.getBackUrl());
		order.setNotifyUrl(req.getNotifyUrl());
		order.setRemark(req.getRemark());
		try {
			fuiouWithdrawDao.insert(order);
		} catch (DuplicateKeyException e) {
			logger.warn(Log.op(op).msg("订单已存在！").kv("orderId", req.getOrderId()).kv("merchantCode", merchantCode).toString(), e);
			order = fuiouWithdrawDao.selectByOrderIdMerchantCode(req.getOrderId(), merchantCode);
		}
		return order;
	}

	@Override
	public FuiouWithdrawModifyCashVO saveModifyWithdrawCashWay(String merchantCode, ModifyWithdrawCashWayReq params) {
		final String op = "FuiouWithdrawServiceImpl.saveModifyWithdrawType";

		FuiouWithdrawModifyCash po = new FuiouWithdrawModifyCash();
		// String busiId = fuiouIdUtils.generateBusiId(FuiouBusiTypeEnum.MODIFY_WITHDRAW_CASH_WAY, merchantCode);
		String busiId = fuiouIdUtils.generateBusiId(FuiouBusiTypeEnum.MODIFY_WITHDRAW_CASH_WAY, merchantCode);
		try {

			po.setBusiId(busiId);
			po.setMerchantCode(merchantCode);
			po.setOrderId(params.getOrderId());
			po.setOrderTime(params.getOrderTime());
			po.setUserId(params.getUserId());
			po.setLoginId(params.getLoginId());
			po.setCashWay(params.getCashWay().getValue());
			po.setBusiType(params.getBusiType().getValue());
			po.setState(FuiouDepositStateEnum.INI.getValue());

			fuiouWithdrawModifyCashDao.insert(po);
		} catch (DuplicateKeyException e) {
			logger.warn(Log.op(op).msg("保存订单信息主键冲突,修改提现方式订单已存在").kv("merchantCode", merchantCode).kv("orderId", params.getOrderId())
					.kv("busiId", busiId).toString(), e);

			po = fuiouWithdrawModifyCashDao.selectByMerchantAndOrderId(merchantCode, params.getOrderId());

			if (po == null) {
				// 保存订单主键冲突，订单表有业务主键异常,一种情况是业务主键异常，还有一种是业务请求订单重复导致
				logger.error(Log.op(op).msg("保存订单信息主键冲突,查询提现方式订单不存在").kv("busiId", busiId).kv("merchantCode", merchantCode)
						.kv("orderId", params.getOrderId()).toString());
				throw PayExceptionCode.ORDER_NOT_FOUND.exp();
			}
		}

		return FuiouWithdrawModifyCashVO.newInstance(po);
	}

	@Override
	public FuiouWithdrawModifyCash doModifyWithdrawCashWay(FuiouWithdrawModifyCashVO vo) {
		final String op = "FuiouWithdrawServiceImpl.doModifyType";
		FuiouWithdrawModifyCash po = vo.getPo();
		if (vo.isResult()) {
			// 如果订单已经出结果则直接返回
			return po;
		}

		// 更新订单状态为处理中
		int i = fuiouWithdrawModifyCashDao.updateState(po.getBusiId(), FuiouDepositStateEnum.INI.getValue(), FuiouDepositStateEnum.DEALING.getValue(),
				PayExceptionCode.PROCESSING.getCode(), PayExceptionCode.PROCESSING.getDesc());
		if (i <= 0) {
			// 如果更新状态没有命中，在并发的情况下，直接返回订单数据
			logger.warn(Log.op(op).msg("更新提现方式，更新状态没有命中").kv("merchantCode", po.getMerchantCode()).kv("orderId", po.getOrderId())
					.kv("origState", FuiouDepositStateEnum.INI.getValue()).kv("destState", FuiouDepositStateEnum.DEALING.getValue()).toString());
			return fuiouWithdrawModifyCashDao.selectByPrimaryKey(po.getBusiId());
		}

		// 更新状态成功，发送请求给富友服务器
		PayRspCodeVO response = fuiouDepositService.doModifyWithdrawCashWay(po);
		int rspCode = response != null ? response.getCode() : PayExceptionCode.ERROR_FILE_FAIL.getCode();
		String rspMsg = response != null ? response.getDesc() : PayExceptionCode.ERROR_FILE_FAIL.getDesc();

		FuiouDepositStateEnum state = FuiouDepositStateEnum.FAIL;
		if (response != null && PayExceptionCode.SUCCESS.isCode(response.getCode())) {
			if (PayExceptionCode.SUCCESS.isCode(response.getCode())) {
				state = FuiouDepositStateEnum.SUCCESS;
			} else if (PayExceptionCode.WARN_REQ_TIME_OUT.isCode(response.getCode())) {
				// 如果是超时，则更新状态为超时
				state = FuiouDepositStateEnum.TIMEOUT;
			}
		}

		po.setState(state.getValue());
		po.setRspCode(rspCode);
		po.setRspMsg(rspMsg);

		// 更新订单状态
		int j = fuiouWithdrawModifyCashDao.updateState(po.getBusiId(), FuiouDepositStateEnum.DEALING.getValue(), state.getValue(), rspCode, rspMsg);
		if (j <= 0) {
			logger.warn(Log.op(op).msg("更新提现方式，更新状态没有命中").kv("merchantCode", po.getMerchantCode()).kv("orderId", po.getOrderId())
					.kv("origState", FuiouDepositStateEnum.DEALING.getValue()).kv("destState", state.getValue()).toString());
		}

		return po;
	}
}
